package sk.datalan.similarity.predicates;

import java.util.List;

public final class Image implements Predicate {
	private final static int balancer = 1000;
	public final static int XSIDE = 6;

	private String value = null;
	private boolean match = false;
	private int[] signature = null;

	public Image( String value ) {
		this.value = value;
	}

	@Override
	public String getId() {
		return "http://www.datalan.sk/semweb/ontology/realestate/1.0/realestate.owl#imageHash";
	}

	@Override
	public double getWeight() {
		return 1; //zatia len tak nahodne hodene ... v skutocnosti by to malo mat ovela vyssiu vahu
	}

	@Override
	public String getValue() {
		return this.value;
	}

	@Override
	public double getSimilarityIndex( List<Predicate> predicates ) {
		double similarity = 0;

		for ( Predicate p : predicates ) {
			similarity = calcDistance( getSignature(), parseSignature( p.getValue() ) );
			if ( similarity < 0.7 ) {
				p.setMatch( true );
				break;
			}
		}

		return similarity;
	}

	public static double calculateDistance( String mainImageHash, String otherImageHash ) {
		return calcDistance( parseSignature( mainImageHash ), parseSignature( otherImageHash ) );
	}

	/*
	 * This method calculates the distance between the signatures of an image
	 * and the reference one. The signatures for the image passed as the
	 * parameter are calculated inside the method.
	 */
	private static double calcDistance( int[] mainSignature, int[] otherSignature ) {
		// There are several ways to calculate distances between two vectors,
		// we will calculate the sum of the distances between the RGB values of
		// pixels in the same positions.

		double dist = 0;
		for ( int x = 0; x < XSIDE; x++ )
			for ( int y = 0; y < XSIDE; y++ ) {
				int r1 = mainSignature[x * XSIDE + y] >>> 16;
				int g1 = mainSignature[x * XSIDE + y] >>> 8 & 0xFF;
				int b1 = mainSignature[x * XSIDE + y] & 0xFF;
				int r2 = otherSignature[x * XSIDE + y] >>> 16;
				int g2 = otherSignature[x * XSIDE + y] >>> 8 & 0xFF;
				int b2 = otherSignature[x * XSIDE + y] & 0xFF;
				double tempDist = Math.sqrt( ( r1 - r2 ) * ( r1 - r2 ) + ( g1 - g2 ) * ( g1 - g2 ) + ( b1 - b2 ) * ( b1 - b2 ) );
				dist += tempDist;
			}
		return ( Math.round( dist * 1000 ) / ( 1000 * balancer ) );
	}

	@Override
	public boolean hasMatch() {
		return this.match;
	}

	@Override
	public void reset() {
		setMatch( false );
	}

	private int[] getSignature() {
		if ( this.signature == null ) {
			this.signature = parseSignature( getValue() );
		}
		return this.signature;
	}

	private static int[] parseSignature( String hashString ) {
		String[] hashes = hashString.split( ";" );
		int[] sig = new int[hashes.length];
		for ( int i = 0; i < hashes.length; i++ ) {
			sig[i] = Integer.parseInt( hashes[i], 16 );
		}
		return sig;
	}

	@Override
	public void setMatch( boolean value ) {
		this.match = true;
	}
}
